package edu.ntu.im.p2ptrade;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import net.jxta.document.AdvertisementFactory;
import net.jxta.endpoint.Message;
import net.jxta.endpoint.StringMessageElement;
import net.jxta.id.IDFactory;
import net.jxta.pipe.InputPipe;
import net.jxta.pipe.OutputPipe;
import net.jxta.pipe.PipeID;
import net.jxta.pipe.PipeMsgEvent;
import net.jxta.pipe.PipeMsgListener;
import net.jxta.pipe.PipeService;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.util.JxtaBiDiPipe;
import edu.ntu.im.p2ptrade.msg.HeartBeat;
import edu.ntu.im.p2ptrade.msg.PMessage;
import edu.ntu.im.p2ptrade.msg.handle.PMessageHandle;
import edu.ntu.im.p2ptrade.msg.handle.PMessageHandleFactory;

public class DialogHost implements PipeMsgListener, Runnable {

	private static final String TO = "to";

	public static final String ALL = "all";


	private Set<String> peers = new HashSet<String>();

	private OutputPipe outputPipe;

	private InputPipe inputPipe;

	private static DialogHost me;

	private Object mutex = new Object();

	private Date lastUpdate = new Date();
	
	 

	public Date getLastUpdate() {
		return lastUpdate;
	}

	private DialogHost() {
		this.setupPipe();
	}

	public static synchronized DialogHost getInstance() {
		if (me == null) {
			me = new DialogHost();
		}
		return me;
	}

	private void setupPipe() {
		PipeAdvertisement propagatePipeAdv = (PipeAdvertisement) AdvertisementFactory
				.newAdvertisement(PipeAdvertisement.getAdvertisementType());

		try {
			byte[] bid = MessageDigest.getInstance("MD5").digest(
					"abcd".getBytes("ISO-8859-1"));
			PipeID pipeID = IDFactory.newPipeID(P2PTrade.getInstance()
					.getAppPeerGroup().getPeerGroupID(), bid);
			propagatePipeAdv.setPipeID(pipeID);
			propagatePipeAdv.setType(PipeService.PropagateType);
			propagatePipeAdv.setName("A chattering propagate pipe");
			propagatePipeAdv.setDescription("verbose description");

			PipeService pipeService = P2PTrade.getInstance().getAppPeerGroup()
					.getPipeService();
			inputPipe = pipeService.createInputPipe(propagatePipeAdv, this);
			outputPipe = pipeService.createOutputPipe(propagatePipeAdv, 1000);
			System.out.println("Propagate pipes and listeners created");
			System.out.println("Propagate PipeID: " + pipeID.toString());
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public boolean hasMeetThePeer(String peer) {
		return false;
	}

	public void meetPeer(String peer) {
		if (!this.peers.contains(peer)){
			this.peers.add(peer);
		}
	}

	public void addPipe(String peer, JxtaBiDiPipe pipe) {
		/*
		 * System.out.println("add pipe with peer:" + peer); synchronized
		 * (mutex) { // if (this.peerPipes.get(peer) == null) {
		 * this.peerPipes.put(peer, pipe); // if (peerMap.get(peer) == null) {
		 * this.meetPeer(peer, pipe.getRemotePipeAdvertisement()); // } // } }
		 */
	}

	public void run() {
		while (true) {
			synchronized (mutex) {
				HeartBeat hb = new HeartBeat(OwnerManager.getOwner());
				Util.transdferMessageToPeer(hb.getMessageToTransfer(), ALL);
				try {
					Thread.sleep(10000);
				} catch (InterruptedException e) { // TODOAuto-generatedcatch
					// block
					e.printStackTrace();
				}
			}
		}
	}

	/*
	 * class PipeConnector implements Runnable { private PipeAdvertisement padv;
	 * 
	 * private String peer;
	 * 
	 * PipeConnector(String peer, PipeAdvertisement adv) { padv = adv; this.peer =
	 * peer; }
	 * 
	 * public void run() { try { System.out.println(" [ trying to connect to
	 * peer [" + padv.getName() + "]"); JxtaBiDiPipe pipe = null; try { pipe =
	 * new JxtaBiDiPipe(P2PTrade.getInstance() .getPeerGroup(), padv, 20000,
	 * DialogManagerUpdated.this, true); } catch (IOException e) {
	 * e.printStackTrace();
	 * System.out.println(P2PTrade.getInstance().getPeerName());
	 * System.out.println(padv); throw e; }
	 * 
	 * DialogManagerUpdated.this.addPipe(peer, pipe);
	 * 
	 * List<Message> msgs = DialogManagerUpdated.this.msgQueues.get(peer); if
	 * (msgs != null) { for (Message msg : msgs) { pipe.sendMessage(msg); } }
	 * System.out.println(" [ connect to peer [" + padv.getName() + "]"); }
	 * catch (IOException e) { // TODO Auto-generated catch block
	 * e.printStackTrace(); } } }
	 */

	public void pipeMsgEvent(PipeMsgEvent event) {
		Message msg = event.getMessage();
		if (msg.getMessageElement(P2PTrade.getInstance().getAppName(), TO) != null) {
			String to = msg.getMessageElement(
					P2PTrade.getInstance().getAppName(), TO).toString();
			if (to.equals(P2PTrade.getInstance().getPeerName())|| to.equals(ALL)) {
				PMessageHandle handle = PMessageHandleFactory.getInstance()
						.getHandle(msg);
					try {
						handle.handle(msg);
					} catch (Exception e) {
						e.printStackTrace();
					}
			}
		}

		// System.out.println(msg.getMessageElement(P2PTrade.appName,
		// GenericDialog.CONTENT));

	}

	// TODO: enhance this!!
	public void sendTo(Message msg, String peer) throws IOException {

		msg.addMessageElement(P2PTrade.getInstance().getAppName(),
				new StringMessageElement(TO, peer, null));
		outputPipe.send(msg);
		/*
		 * msg.addMessageElement(P2PTrade.getInstance().getAppName(), new
		 * StringMessageElement(CONTENT, content, null));
		 */
		/*
		 * Set<String> peers = peerMap.keySet(); for (String cur : peers) {
		 * System.out.println("miao hah hah hah peer:" + cur); }
		 * 
		 * if (this.peerMap.get(peer) != null) { if (this.peerPipes.get(peer) !=
		 * null) { JxtaBiDiPipe bidiPipe = this.peerPipes.get(peer);
		 * bidiPipe.sendMessage(msg); } else { List<Message> msgs =
		 * this.msgQueues.get(peer); if (msgs == null) { msgs = new LinkedList<Message>(); }
		 * msgs.add(msg); this.msgQueues.put(peer, msgs); } } else { throw new
		 * NerverMeetThisPeerException("The peer[" + peer + "] is never meet"); }
		 */

	}

	public Set<String> getAllPeers() {
		return this.peers;

	}

	public  boolean haveMeetPeer(String peer) {
		return this.peers.contains(peer);
	}

}
